﻿using Levfier.Proccessors.Parameters;
using System;

namespace Levfier.Proccessors.Filters
{
    /// <summary>
    /// IIRフィルタの基本クラス
    /// </summary>
    [Serializable]
    abstract public class IIR : Proccessor
    {
        /// <summary>
        /// フィルタ段数
        /// </summary>
        protected readonly static int RowSize = 3;

        /// <summary>
        /// カットオフ周波数
        /// </summary>
        protected ProccessParameter cutOffFrequencyParam;

        /// <summary>
        /// クオリティファクタ
        /// </summary>
        protected ProccessParameter qualityFactorParam;

        /// <summary>
        /// フィルタ係数
        /// </summary>
        protected double a1, a2, b0, b1, b2;

        /// <summary>
        /// 入力遅延バッファ
        /// </summary>
        protected double[] x;

        /// <summary>
        /// 出力遅延バッファ
        /// </summary>
        protected double[] y;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public IIR()
        {
            VSTParameterManager.GetInstance().SampleRateChanged += ChangeSampleRate;
            x = new double[] { 0.0d, 0.0d, 0.0d };
            y = new double[] { 0.0d, 0.0d, 0.0d };
            this.InitFilterCoefficient(this);
        }

        /// <summary>
        /// 入力を線形増幅する
        /// </summary>
        /// <param name="input">入力</param>
        /// <param name="output">出力</param>
        protected override void InnerProccess(double[] inputBuffer, double[] outputBuffer)
        {
            //計算
            for (int i = 0; i < inputBuffer.Length; i++)
            {
                //1サンプル分ずらす
                x[2] = x[1];
                x[1] = x[0];
                y[2] = y[1];
                y[1] = y[0];

                x[0] = inputBuffer[i];

                //インプット
                y[0] = (b0 * x[0]) + (b1 * x[1]) + (b2 * x[2]) - (a1 * y[1]) - (a2 * y[2]);
                outputBuffer[i] = y[0];
            }
        }

        /// <summary>
        /// パラメータを初期化する
        /// </summary>
        protected override void InitParameters()
        {
            this.cutOffFrequencyParam = new ProccessParameter();
            this.cutOffFrequencyParam.Name = "Freq[Hz]";
            this.cutOffFrequencyParam.Value = 1000.0d;
            this.cutOffFrequencyParam.MaxValue = 16000.0d;
            this.cutOffFrequencyParam.MinValue = 10.0d;
            this.cutOffFrequencyParam.Step = 10.0d;
            this.parameters.Add(this.cutOffFrequencyParam.Name, this.cutOffFrequencyParam);

            this.qualityFactorParam = new ProccessParameter();
            this.qualityFactorParam.Name = "QFactor";
            this.qualityFactorParam.Value = 0.8d;
            this.qualityFactorParam.MaxValue = 10.0d;
            this.qualityFactorParam.MinValue = 0.5d;
            this.qualityFactorParam.Step = 0.1d;
            this.parameters.Add(this.qualityFactorParam.Name, this.qualityFactorParam);

            //イベントハンドラ登録
            this.cutOffFrequencyParam.ValueChanged += this.InitFilterCoefficient;
            this.qualityFactorParam.ValueChanged += this.InitFilterCoefficient;
        }

        /// <summary>
        /// フィルタ係数を初期化する
        /// </summary>
        /// <param name="sender">イベント発生源</param>
        abstract protected void InitFilterCoefficient(object sender);

        /// <summary>
        /// サンプルレートが変更された時の処理
        /// </summary>
        /// <param name="value">変化後の値</param>
        private void ChangeSampleRate(double value)
        {
            this.InitFilterCoefficient(this);
        }
    }
}
